﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Diagnostics;
using log4net;
using System.Runtime.Serialization;
using RollBackLib.src.utils;
using RollbackLib.src.utils;

/**
 * serialize and de-serialize the backup set information
 */
namespace RollBackLib.src.core {

    /// <summary>
    /// A job's id like Name, Guid etc.
    /// </summary>
    [Serializable]
    public class JobId
    {
        private string m_name;
        public string  m_Name
        {
            get
            {
                return m_name;
            }
            set
            {
                m_Modified = true;
                m_name = value;
            }
        }

        private Guid m_guid;
        public Guid m_Guid
        {
            get
            {
                return m_guid;
            }
            set
            {
                m_Modified = true;
                m_guid = value;
            }
        }

        private bool m_modified;
        public bool m_Modified
        {
            get
            {
                return m_modified;
            }
            set
            {
                m_modified = value;
            }
        }

        public JobId()
        {
            m_Guid = Guid.NewGuid();
            m_Name = "";

        }

    }

    /// <summary>
    /// Regular expression file/folder filter
    /// </summary>
    [Serializable()]
    public class RegexFilter
    {
        private bool m_modified;
        public bool m_Modified
        {
            get { return m_modified; }
            set
            {
                m_modified = value;
            }
        }

        private bool m_enabled = true;
        public bool m_Enabled
        {
            get
            {
                return m_enabled;
            }
            set
            {
                m_modified = true;
                m_enabled = value;
            }
        }

        private List<string> m_expressions = new List<string>();
        public List<string> m_Expressions
        {
            get
            {
                return m_expressions;
            }
            set
            {
                m_modified = true;
                m_expressions = value;
            }
        }
    }

    /// <summary>
    /// Represents a single backup set, including the list of files/folders to be backed up and the
    /// destination directory.
    /// </summary>
    [Serializable()]
    public class BackupJob {
        private static readonly ILog log = LogManager.GetLogger(typeof(BackupJob));

        private JobId m_id;

        public JobId m_Id
        {
            get
            {
                return m_id;
            }
            set
            {
                m_Modified = true;
                m_id = value;
            }
        }

        /// <summary>
        /// The current run status of the backup job, one of Running, Paused, NotRunning, Unknown,
        /// </summary>
        public BackupInfo.RunStatus m_RunningStatus
        {
            get;
            set;
        }

        private string m_destDir;
        /// <summary>
        /// The destination directory where the backed files will go.
        /// </summary>
        public string m_DestDir
        {
            get { return m_destDir; }
            set
            {
                m_destDir = value;
                m_Modified = true;
            }
        }

        /// <summary>
        /// The list of emailids to which an email should be sent when the job finishes.
        /// </summary>
        private List<String> m_emailList;
        public List<String> m_EmailList
        {
            get { return m_emailList; }
            set
            {
                m_emailList = value;
            }
        }

        private List<BackupItem> m_backupPathList;
        /// <summary>
        /// The list of paths to be backed up
        /// </summary>
        public List<BackupItem> m_BackupPathList
        {
            get 
            {
                return m_backupPathList;
            }
            set 
            {
                m_Modified = true;
                m_backupPathList = value;
            }
        }

        List<BackupItem> m_restorePathList;
        /// <summary>
        /// List of paths to be restored.
        /// </summary>
        public List<BackupItem> m_RestorePathList
        {
            get
            {
                return m_restorePathList;
            }
            set
            {
                m_restorePathList = value;
                m_Modified = true;
            }
        }

        /// <summary>
        /// Whether this job is enabled or not
        /// </summary>
        public bool m_Enabled {
            get;
            set;
        }

        /// <summary>
        /// Set to true if the job was deleted 
        /// </summary>
        public bool m_Deleted
        {
            get;
            set;
        }

        private bool m_modified;
        /// <summary>
        /// Flag to indicate whether the job data was modified or not
        /// </summary>
        public bool m_Modified
        {
            get
            {
                bool value = 
                    m_modified ||
                    m_includeFilePattern.m_Modified ||
                    m_includeFolderPattern.m_Modified ||
                    m_excludeFilePattern.m_Modified ||
                    m_excludeFolderPattern.m_Modified ||
                    m_JobSchedule.m_Modified;
                if (m_id != null)
                {
                    value |= m_id.m_Modified;
                }
                if (m_JobSchedule != null)
                {
                    value |= m_JobSchedule.m_Modified;
                }
                
                return value;
            }
            set 
            {
                m_modified = value;
                if (value == false)
                {
                    // only set the false value for sub items of the job.
                    if (m_id != null)
                    {
                        m_id.m_Modified = value;
                    }
                    if (m_JobSchedule != null)
                    {
                        m_JobSchedule.m_Modified = value;
                    }
                    m_includeFilePattern.m_Modified = value;
                    m_includeFolderPattern.m_Modified = value;
                    m_excludeFilePattern.m_Modified = value;
                    m_excludeFolderPattern.m_Modified = value;
                }
            }
        }

        /// <summary>
        /// The last attempt to run the job (could have been an unsuccessful one).
        /// </summary>
        public DateTime m_lastRunAttempted;
        /// <summary>
        /// Result of last run
        /// 0 - not run
        /// 1 - failure
        /// 2 - success
        /// 3 - Aborted
        /// </summary>
        public BackupInfo.LastRunResult m_lastRunSuccess = BackupInfo.LastRunResult.FAILED;

        // Regular expression patterns for the filter
        public RegexFilter m_includeFilePattern = new RegexFilter();
        public RegexFilter m_excludeFilePattern = new RegexFilter();
        public RegexFilter m_includeFolderPattern = new RegexFilter();
        public RegexFilter m_excludeFolderPattern = new RegexFilter();

        public string m_runHowOften;
        public string m_runWhatDay;
        public string m_runWhatTime;
        public bool m_extraLoggingEnabled
        {
            get;
            set;
        }

        /// <summary>
        /// Date and time when the job was originally created.
        /// </summary>
        public DateTime m_createDate;

        public JobSchedule m_JobSchedule
        {
            get;
            set;
        }

        private bool m_overwriteNewerFiles;
        /// <summary>
        /// if this flag is truned on then newer files will in the backup directory will be overwritten 
        /// </summary>
        public bool m_OverwriteNewerFiles
        {
            get
            {
                return m_overwriteNewerFiles;
            }
            set
            {
                m_modified = true;
                m_overwriteNewerFiles = value;
            }
        }

        private bool m_synchronizeBackups;
        /// <summary>
        /// Keep backup and original in sync, i.e. prune any files and folders in the Backed up folder that are no longer
        /// part of the original source.
        /// </summary>
        public bool m_SynchronizeBackups
        {
            get
            {
                return m_synchronizeBackups;
            }
            set
            {
                m_modified = true;
                m_synchronizeBackups = value;
            }
        }

        private object m_data;

        public BackupJob()
        {
            m_RunningStatus = BackupInfo.RunStatus.NotRunning;
            m_Id = new JobId();
            m_JobSchedule = new JobSchedule();
            m_DestDir = null;
            m_BackupPathList = new List<BackupItem>();
            m_RestorePathList = new List<BackupItem>();
            m_Enabled = true;
            m_Deleted = false;
            m_SynchronizeBackups = false;
        }

        /// <summary>
        /// Load backup sets from the root backup XML file.
        /// </summary>
        public static List<BackupJob> LoadBackupSets(string rootPath)
        {
            log.Info("Loading Backup Jobs from <" + rootPath+ ">");
            List<BackupJob> l = new List<BackupJob>();
            List<BackupJob> newList = new List<BackupJob>();
            if (File.Exists(rootPath))
            {
                l = (List<BackupJob>)XMLUtils.DeSerializeFromXML(rootPath, l.GetType());
                foreach (BackupJob job in l)
                {
                    if (job.validate())
                    {
                        newList.Add(job);
                    }
                    else
                    {
                        log.Info("Ignoring job <" + job.GetKey() + ">, since it is not valid");
                    }
                }
            }
            else
            {
                log.Info("Cannot deserialize.  File <" + rootPath + "> does not exist");
            }
            return newList;
        }

        /// <summary>
        /// Validate the BackupJob data
        /// </summary>
        public bool validate()
        {
            bool valid = true;
            if (m_Deleted)
            {
                valid = false;
            }
            else
            {
                m_JobSchedule.validateSchedule();
            }
            return valid;
        }

        /// <summary>
        /// Update the list of top level entries in the backup path.
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public bool updateBackupPathList(List<BackupItem> list) {
            this.m_BackupPathList = list;
            m_Modified = true;
            return true;
        }

        public string GetKey()
        {
            string key = "Name=<" + m_Id.m_Name + "> Guid=<" + m_Id.m_Guid + "> Dest Dir=<" + m_DestDir + ">";
            return key;
        }


        /// <summary>
        /// Return true if it is time to schedule this job
        /// </summary>
        internal bool timeToSchedule()
        {
            bool v = false;
            if (m_Enabled && m_JobSchedule!=null && m_JobSchedule.m_Enabled)
            {
                v = m_JobSchedule.timeToSchedule(m_lastRunAttempted);
            }
            return v;
        }

        public bool isRunning()
        {
            bool value = 
                (m_RunningStatus != BackupInfo.RunStatus.NotRunning);
            return value;
        }

        /// <summary>
        /// Update the time when the last run attempt was started.
        /// </summary>
        internal void registerRunStart()
        {
            m_lastRunAttempted = DateTime.Now;
            m_lastRunSuccess = BackupInfo.LastRunResult.FAILED;
            m_RunningStatus = BackupInfo.RunStatus.Running;
        }

        internal void registerRunAbort()
        {
            m_lastRunSuccess = BackupInfo.LastRunResult.ABORTED;
            m_RunningStatus = BackupInfo.RunStatus.NotRunning;
        }

        internal void registerRunStop()
        {
            m_lastRunSuccess = BackupInfo.LastRunResult.FINISHED;
            m_RunningStatus = BackupInfo.RunStatus.NotRunning;
        }

        internal void registerRunPause()
        {
            m_RunningStatus = BackupInfo.RunStatus.Paused;
        }

        internal void registerRunResume()
        {
            m_RunningStatus = BackupInfo.RunStatus.Running;
        }

        /// <summary>
        /// Reset some member variables which are sometimes incorrectly set at save time
        /// NOTE: This function should be called with caution as it changes the internal state of the job.
        /// </summary>
        internal void resetJobSettings()
        {
            m_RunningStatus = BackupInfo.RunStatus.NotRunning;
        }

        /// <summary>
        /// Return the final destination directory where the backed up files will go.
        /// </summary>
        /// <returns></returns>
        public string getFinalDestinationDir()
        {
            string dest = null;
            if (m_DestDir != null)
            {
                dest = m_DestDir.Trim() + "\\" + m_Id.m_Name;
            }
            return dest;
        }

        public object getData()
        {
            return m_data;
        }

        public void setData(object data)
        {
            m_data = data;
        }

        /// <summary>
        /// Add a path to the backup job
        /// </summary>
        /// <param name="p"></param>
        public void addPath(string p)
        {
            m_Modified = true;
            BackupItem item = new BackupItem(p,true);
            m_BackupPathList.Add(item);
        }

        public void initialize(string destDir, string jobName)
        {
            m_DestDir = destDir;
            m_Id.m_Name = jobName;
        }
    }
}
